package com.investmenttechnologyservices.services;

import java.util.ArrayList;
import java.util.Currency;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.Filter;
import com.google.appengine.api.datastore.Query.FilterOperator;
import com.google.appengine.api.datastore.Query.FilterPredicate;
import com.google.inject.Inject;
import com.investmenttechnologyservices.model.Instrument;
import com.investmenttechnologyservices.model.PriceFormat;
import com.investmenttechnologyservices.model.Symbol;

public class SymbolServiceImpl implements SymbolService {


	private final Key itsKey = KeyFactory.createKey("dataProvider", "ITS");
	private final DatastoreService ds;
	private final ExchangeService exchangeService;
	
	@Inject
	SymbolServiceImpl(DatastoreService ds, ExchangeService exchangeService) {
		this.ds = ds;
		this.exchangeService = exchangeService;
	}

	
	/**
	 * returns a list of Maps which represent symbols
	 * each map contains the following properties: 
	 * id - the string id of the symbol
	 * instrument - the instrument name 
	 * currency - the currency code
	 * 
	 * @return list of symbols. 
	 * 
	 * @throws ServletException
	 */
	@Override
	public List<Map<String,Object>> pickerFindAll(HttpServletRequest req, HttpServletResponse res) throws ServletException {
		try {
			// get ITS symbols only
			Key dataProviderKey = KeyFactory.createKey("dataProvider", "ITS");
			Filter dataProviderFilter = new FilterPredicate("dataProviderKey",FilterOperator.EQUAL,dataProviderKey);
			
			Query q = new Query("symbol").setFilter(dataProviderFilter);
			PreparedQuery pq = ds.prepare(q);
			Iterable<Entity> iter = pq.asIterable();
			List<Map<String,Object>> ret = new ArrayList<Map<String,Object>>();
			for (Entity entity : iter) {
				Map<String,Object> element = new HashMap<String, Object>();
				Key symbolKey = entity.getKey();
				Key instrumentKey = symbolKey.getParent();
				if (instrumentKey != null) {
					String instString = getInstrumentName(instrumentKey);
					element.put("instrument", instString);
					element.put("id", instrumentKey.getName()+';'+symbolKey.getName());
				} else {
					element.put("id", ';'+symbolKey.getName());
				}
				element.put("symbol", symbolKey.getName());
				element.put("currency", entity.getProperty("currency"));
				ret.add(element);
			}
			return ret;
		} catch (Exception ex) {
			throw new ServletException(ex);
		}
	}
	
	// helper method : retrieve the name of the instrument from the instrument key
	private String getInstrumentName(Key instrumentKey) {
		String instString;
		try {
//			System.err.println("calling ds.get on " + instrumentKey);
			Entity instrumentEntity = ds.get(instrumentKey);
			instString = (String)instrumentEntity.getProperty("name");
//			System.err.println("---->" + instString);
		} catch (EntityNotFoundException ex) {
			instString = instrumentKey.getName() + " (name not found)";
		}
		return instString;
	}

	@Override
	public PriceFormat getInstrumentPriceFormat(Key instrumentKey) {
		PriceFormat priceFormat;
		try {
			Entity instrumentEntity = ds.get(instrumentKey);
			priceFormat = PriceFormat.valueOf((String)instrumentEntity.getProperty("priceFormat"));
		} catch (EntityNotFoundException ex) {
			priceFormat = PriceFormat.SIMPLE;
		}
		return priceFormat;
	}

	@Override
	public Date calculateSettlementDate(Key symbolKey, Date tradeDate) {
		Date settlementDate = null;
		if (tradeDate != null) {
			try {
				Key instrumentKey = symbolKey.getParent();
				Instrument instrument = new Instrument(ds.get(instrumentKey));
				Symbol symbol = new Symbol(ds.get(symbolKey));
				Key exchangeKey = symbol.getExchangeKey();
				settlementDate = 
					exchangeService.calculateSettlementDate(exchangeKey, tradeDate, instrument.getInstrumentType());
			} catch (EntityNotFoundException ex) {
				// TODO Auto-generated catch block
				ex.printStackTrace();
			}
		}
		return settlementDate;
	}

	@Override
	public Key createSymbolITS(Key instrumentKey, String id, Key exchangeKey, Currency currency) {
		Symbol symbol = new Symbol(id, instrumentKey,  itsKey, exchangeKey, currency);
		ds.put(symbol.asEntity());
		return symbol.getKey();
	}

	@Override
	public Key getSymbol(Key instrumentKey, Key dataProviderKey) {

		Key symbolKey = null;
		
		Filter dataProviderFilter = new FilterPredicate("dataProviderKey",FilterOperator.EQUAL,dataProviderKey);
		
		Query q = new Query("symbol").
				setAncestor(instrumentKey).
				setFilter(dataProviderFilter);
		FetchOptions fo = FetchOptions.Builder.withDefaults();
		List<Entity> symbols = ds.prepare(q).asList(fo);
		if (symbols.size()!=0)
			symbolKey = symbols.get(0).getKey();
		return symbolKey;
	}

	@Override
	public PriceFormat getPriceFormat(Key symbolKey) {
		Key instrumentKey;
		PriceFormat priceFormat = null;
		try {
			priceFormat = new Symbol(ds.get(symbolKey)).getPriceFormat();
			// if not defined at the symbol level get from the instrument
			if (priceFormat == null) {
				instrumentKey = symbolKey.getParent();
				priceFormat = new Instrument(ds.get(instrumentKey)).getPriceFormat();
			}
		} catch (EntityNotFoundException ex) {
			ex.printStackTrace();
		}
		
		return priceFormat;
	}
}
